package org.totoro.concurrent.juc;

import org.junit.Before;
import org.junit.Test;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.Date;

/**
 * @author daocr
 * @date 2019-06-06
 */
public class UnsafeTest {

    private Unsafe unsafe;

    /**
     * 获取 unsafe 对象
     *
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    @Before
    public void getUnsafe() throws NoSuchFieldException, IllegalAccessException {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        unsafe = (Unsafe) theUnsafe.get(Unsafe.class);
    }


    /**
     * ••
     * 获取静态变量，偏移量
     *
     * @see Unsafe#staticFieldOffset(Field)
     * <p>
     * <p>
     * 获取对象成员变量，偏移量
     * @see Unsafe#objectFieldOffset(Field)
     * <p>
     * <p>
     * 获取数组偏移量
     * @see Unsafe#arrayBaseOffset(Class)
     */
    @Test
    public void getOffset() throws NoSuchFieldException {

        getstaticVariableOffset();

        OffsetObject offsetObject = new OffsetObject();

        getlocalVariableOffset(offsetObject);

        getVolatileLocalVariableOffset(offsetObject);


    }

    /**
     * 获取 局部变量偏移量
     *
     * @throws NoSuchFieldException
     */
    private Long getlocalVariableOffset(OffsetObject offsetObject) throws NoSuchFieldException {

        // 局部变量
        Field localVariable = offsetObject.getClass().getDeclaredField("localVariable");
        long objectFieldOffset = unsafe.objectFieldOffset(localVariable);
        System.out.println("localVariable offset :" + objectFieldOffset);

        return objectFieldOffset;
    }

    /**
     * 获取静态变量 偏移量
     *
     * @return
     * @throws NoSuchFieldException
     */
    private Long getstaticVariableOffset() throws NoSuchFieldException {
        // 静态变量
        Field staticVariable = OffsetObject.class.getDeclaredField("staticVariable");
        long staticFieldOffset = unsafe.staticFieldOffset(staticVariable);
        System.out.println("staticVariable offset :" + staticFieldOffset);

        return staticFieldOffset;
    }


    private Long getVolatileLocalVariableOffset(OffsetObject offsetObject) throws NoSuchFieldException {

        // 局部变量
        Field volatileLocalVariable = offsetObject.getClass().getDeclaredField("volatileLocalVariable");
        long volatileLocalVariableOffset = unsafe.objectFieldOffset(volatileLocalVariable);
        System.out.println("volatileLocalVariable offset :" + volatileLocalVariableOffset);

        return volatileLocalVariableOffset;
    }


    /**
     * 修改 内存值
     * <p>
     * <p>
     * 修改基础数据类型，修改包装类会报错
     *
     * @see Unsafe#putByte(Object, long, byte)
     * @see Unsafe#putByteVolatile(Object, long, byte)
     * <p>
     * 修改包装类数据、数组、对象
     * @see Unsafe#putObject(Object, long, Object)
     * 存储变量的引用到对象的指定的偏移量处，使用volatile的存储语义
     * @see Unsafe#putObjectVolatile(Object, long, Object)
     */
    @Test
    public void putValue() throws NoSuchFieldException {

        OffsetObject localVariableOffset = new OffsetObject();

        // 修改成员变量
        Long variableOffset = getlocalVariableOffset(localVariableOffset);
        System.out.println("localVariable 修改前：" + localVariableOffset.localVariable);
        unsafe.putObject(localVariableOffset, variableOffset, 100);
        System.out.println("localVariable 修改后：" + localVariableOffset.localVariable);

        System.out.println("\n");

        // 修改 volatileLocalVariableO  value
        Long volatileLocalVariableOffset = getVolatileLocalVariableOffset(localVariableOffset);
        System.out.println("volatileLocalVariable 修改前：" + localVariableOffset.volatileLocalVariable);
        unsafe.putObjectVolatile(localVariableOffset, volatileLocalVariableOffset, 300);
        System.out.println("volatileLocalVariable 修改后：" + localVariableOffset.volatileLocalVariable);


        System.out.println("\n");
        // 修改静态变量
        Long staticVariableOffset = getstaticVariableOffset();
        System.out.println("staticVariable 修改前：" + OffsetObject.staticVariable);
        unsafe.putObject(OffsetObject.class, staticVariableOffset, 200);
        System.out.println("staticVariable 修改后：" + OffsetObject.staticVariable);

    }

    /**
     * 获取 内存值
     * 获取基础类型 int,boolean,.... ,获取包装类型，会报错
     *
     * @see Unsafe#getBoolean(Object, long)
     * @see Unsafe#getBooleanVolatile(Object, long)
     * <p>
     * 获取 包装类型、数组、对象
     * @see Unsafe#getObject(Object, long)
     * 从对象的指定偏移量处获取变量的引用，使用volatile的加载语义
     * @see Unsafe#getObjectVolatile(Object, long)
     */
    @Test
    public void getValue() throws NoSuchFieldException {

        OffsetObject localVariableOffset = new OffsetObject();
        // 获取局部变量
        Long variableOffset = getlocalVariableOffset(localVariableOffset);
        Integer localVariableValue = (Integer) unsafe.getObject(localVariableOffset, variableOffset);
        System.out.println("localVariableValue :" + localVariableValue);


        System.out.println("\n");
        Long volatileLocalVariableOffset = getVolatileLocalVariableOffset(localVariableOffset);
        Integer volatileLocalVariableValue = (Integer) unsafe.getObjectVolatile(localVariableOffset, volatileLocalVariableOffset);
        System.out.println("volatileLocalVariableValue :" + volatileLocalVariableValue);

        System.out.println("\n");
        // 获取静态变量
        Long staticVariableOffset = getstaticVariableOffset();
        Integer staticVariableValue = (Integer) unsafe.getObject(OffsetObject.class, staticVariableOffset);
        System.out.println("staticVariableValue :" + staticVariableValue);
    }

    /**
     * 阻塞当前线程
     * <p>
     * boolean：true > 指定一个未来时间（毫秒）；false > 堵塞 指定 纳秒数
     * long：休眠时间
     *
     * @see Unsafe#park(boolean, long)
     */
    @Test
    public void park() {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

        System.out.println(Thread.currentThread().getName() + "堵塞时间：" + sdf.format(new Date()));
        unsafe.park(true, System.currentTimeMillis() + 5000);
        System.out.println(Thread.currentThread().getName() + "唤醒时间：" + sdf.format(new Date()));

        System.out.println();

        System.out.println(Thread.currentThread().getName() + "堵塞时间：" + sdf.format(new Date()));
        unsafe.park(false, Duration.ofSeconds(5).toNanos());
        System.out.println(Thread.currentThread().getName() + "唤醒时间：" + sdf.format(new Date()));
    }

    /**
     * 内存屏障
     * <p>
     * <p>
     * 内存屏障，禁止load操作重排序。屏障前的load操作不能被重排序到屏障后，屏障后的load操作不能被重排序到屏障前
     *
     * @see Unsafe#loadFence()
     * 内存屏障，禁止store操作重排序。屏障前的store操作不能被重排序到屏障后，屏障后的store操作不能被重排序到屏障前
     * @see Unsafe#storeFence()
     * //内存屏障，禁止load、store操作重排序
     * @see Unsafe#fullFence()
     */
    public void memory() {

    }

    /**
     * 获取对象头像信息
     * <p>
     * 启用偏向锁 -XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
     * 关闭偏向锁 -XX:-UseBiasedLocking
     * <p>
     * 启用自旋锁 -XX:+UseSpinning -XX:PreBlockSpin=10 自旋次数10次
     * 关闭自旋锁 -XX:-UseSpinning
     *
     *
     *
     * <p>
     * <img  src="markword.png"/>
     * <pre>
     *     32 bits:
     * |-------------------------------------------------------|--------------------|
     * |                  Mark Word (32 bits)                  |       State        |
     * |-------------------------------------------------------|--------------------|
     * | identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 |       Normal       |
     * |-------------------------------------------------------|--------------------|
     * |  thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 |       Biased       |
     * |-------------------------------------------------------|--------------------|
     * |               ptr_to_lock_record:30          | lock:2 | Lightweight Locked |
     * |-------------------------------------------------------|--------------------|
     * |               ptr_to_heavyweight_monitor:30  | lock:2 | Heavyweight Locked |
     * |-------------------------------------------------------|--------------------|
     * |                                              | lock:2 |    Marked for GC   |
     * |-------------------------------------------------------|--------------------|
     *
     * </pre>
     *
     * <pre>
     *     64 bits:
     * |------------------------------------------------------------------------------|--------------------|
     * |                                  Mark Word (64 bits)                         |       State        |
     * |------------------------------------------------------------------------------|--------------------|
     * | unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |       Normal       |
     * |------------------------------------------------------------------------------|--------------------|
     * | thread:54 |       epoch:2        | unused:1 | age:4 | biased_lock:1 | lock:2 |       Biased       |
     * |------------------------------------------------------------------------------|--------------------|
     * |                       ptr_to_lock_record:62                         | lock:2 | Lightweight Locked |
     * |------------------------------------------------------------------------------|--------------------|
     * |                     ptr_to_heavyweight_monitor:62                   | lock:2 | Heavyweight Locked |
     * |------------------------------------------------------------------------------|--------------------|
     * |                                                                     | lock:2 |    Marked for GC   |
     * |------------------------------------------------------------------------------|--------------------|
     *
     *
     * </pre>
     * <p>
     * <pre>
     * biased_lock	    lock                状态
     * 0	            01	                无锁
     * 1	            01	                偏向锁
     * 0	            00	                轻量级锁
     * 0	            10	                重量级锁
     * 0	            11	                GC标记
     * </pre>
     *
     *
     * <pre>
     * 偏向锁 ：
     *      优点：加锁和解锁不需要额外的消耗，和执行非同步方法比仅存在纳秒级的差距。
     *      缺点：如果线程间存在锁竞争，会带来额外的锁撤销的消耗。
     *      适用场景： 适用于只有一个线程访问同步块场景。
     *
     * 轻量级锁
     *      优点：竞争的线程不会阻塞，提高了程序的响应速度。
     *      缺点：如果始终得不到锁竞争的线程使用自旋会消耗CPU。
     *      适用场景：追求响应时间。 同步块执行速度非常快。
     *
     * 重量级锁
     *
     *      优点：线程竞争不使用自旋，不会消耗CPU。
     *      缺点：线程阻塞，响应时间缓慢。
     *      适用场景：追求吞吐量,同步块执行速度较长。
     *
     *
     * </pre>
     *
     * <img src="lockConversion.png" width="100px"/>
     */
    @Test
    public void getMarkWord() throws InterruptedException {

        int i = unsafe.addressSize();

        switch (i) {
            case 4:
                System.out.println("32 位系统");
                break;
            case 8:
                System.out.println("64位系统");
                break;
            default:
                System.out.println("无法识别 操作系统");
        }


        OffsetObject offsetObject = new OffsetObject();

        print("无锁", offsetObject);


        // 升级到偏向锁
        synchronized (offsetObject) {
            print("偏向锁", offsetObject);
        }

        new Thread(() -> {
            synchronized (offsetObject) {
                print("轻量级锁", offsetObject);
            }
        }).start();

        Thread.sleep(1000);

        synchronized (offsetObject) {
            //  升级到重量级别锁
            new Thread(() -> {
                synchronized (offsetObject) {
                    print("重量级别锁", offsetObject);
                }
            }).start();

            Thread.sleep(1000);
        }

    }

    private void print(String lockName, OffsetObject offsetObject) {

        long markWorkValue = unsafe.getLong(offsetObject, 0L);

        /**
         * 锁状态
         */
        long lockCodeMod = 3L;
        /**
         * 是否锁偏向锁
         */
        long biasedLockMod = 4L;


        System.out.println(
                "lock name : " + lockName +
                        "\t\t mark work:" + Long.toBinaryString(markWorkValue)
                        + "\t\t lock code :" + Long.toBinaryString(markWorkValue & lockCodeMod)
                        + "\t\t  biased code :" + Long.toBinaryString(markWorkValue & biasedLockMod));

    }

    /**
     * 要修改的对象，字段偏移量，内存值，更新值
     *
     * @see Unsafe#compareAndSwapObject(Object, long, Object, Object)
     */
    @Test
    public void cas() throws NoSuchFieldException {

        OffsetObject offsetObject = new OffsetObject();

        Long offset = getlocalVariableOffset(offsetObject);

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                boolean b = unsafe.compareAndSwapObject(offsetObject, offset, 2, 5);
                System.out.println(Thread.currentThread().getName() + "修改状态：" + b);
            }).start();
        }

    }


    private static class OffsetObject {

        /**
         * 静态变量
         */
        private static Integer staticVariable = 1;

        /**
         * 成员变量
         */
        private Integer localVariable = 2;

        /**
         * volatile 成员变量
         */
        private volatile Integer volatileLocalVariable = 3;
    }


}
